package com.sakura.demo.auth.config;

import com.sakura.demo.auth.jwt.JwtTokenEnhancer;
import com.sakura.demo.auth.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: zhengcan
 * @Date: 2022/5/14
 * @Description: 授权认证服务器：token存入JDBC数据库模式
 * @Version: 1.0.0 创建
 */
@Component
@EnableAuthorizationServer
public class SakuraJdbcAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManagerBean;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;
    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private TokenStore sakuraTokenStore;

    /**
     * 给jdbc模式的ClientDetailsService
     *  服务配置数据源处理client相关信息的存取，需要数据库提前有值才可取出比对
     * @return
     */
    @Bean
    public ClientDetailsService jdbcClientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 已授权客户端存储记录
     * @return
     */
    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }
    /**
     * 授权码code的存储于mysql中
     *  注：授权码模式时使用
     * @return
     */
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 配置token的处理方式为jdbc模式
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 整合JWT token
        List<TokenEnhancer> delegates = new ArrayList<>();
        // 配置一个自定义的jwt token增强器
        delegates.add(jwtTokenEnhancer);
        // 配置token转换器
        delegates.add(accessTokenConverter);
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(delegates);

        endpoints
                // 配置token存储方式为jdbc
                .tokenStore(sakuraTokenStore)
                // 已授权客户端存储配置
                .approvalStore(approvalStore())
                // 授权码模式时验证配置，grant_type为authorization_code模式时必须配置
                .authorizationCodeServices(authorizationCodeServices())
                // 密码模式时验证管理器, grant_type为password模式时必须配置
                .authenticationManager(authenticationManagerBean)
                // 自定义的用户信息查询服务实现
                .userDetailsService(userDetailsService)
                // 加入jwt token增强器
                .accessTokenConverter(accessTokenConverter)
                // token转换器
                .tokenEnhancer(enhancerChain)
                // 配置客户端信息来源
                .setClientDetailsService(jdbcClientDetailsService());
    }

    /**
     * 配置client相关信息的处理方式为jdbc模式
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 配置Endpoint,允许请求，不被Spring-security拦截
        security
                // 允许表单认证, 允许密码授权模式时进行用户的登录验证，需要表单提交的方式
                .allowFormAuthenticationForClients()
                // 开启/oauth/token_key 权限为：无需验证端口访问权限，无服务远程调用该接口时可不开启
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token 权限为：需要验证端口访问权限，无服务远程调用该接口时可不开启
                .checkTokenAccess("isAuthenticated()")
                // 配置BCrypt加密
                .passwordEncoder(passwordEncoder);
    }

}
